feat(doc-index): infer real section granularity, hash sections for staleness - #1
Open
tkcoding wants to merge 3 commits into
Open
feat(doc-index): infer real section granularity, hash sections for staleness#1tkcoding wants to merge 3 commits into
tkcoding wants to merge 3 commits into
Conversation
added 3 commits
August 28, 2026 15:15
…aleness Neither of these existed before: doc_index.py indexed every heading at every level, with no notion of "one retrievable section", and its staleness check was whole-file only -- any edit anywhere invalidated the entire cached index, making a real per-section partial rebuild impossible regardless of how small the actual edit was. infer_section_level() picks which heading level represents one real section, using the level's *frequency* as the signal: a document's real recurring structure (its chapters) shows up as the level used most often, while an occasional heading at an anomalous level -- exactly what PDF-to-Markdown conversion produces, since it assigns levels by font-size heuristics, not semantic depth -- is rare precisely because it's noise, not structure. Levels used only once are excluded as candidates outright. This is a direct, verified fix for a real failure found earlier building this feature: a real PDF-converted document put all 8 of its actual chapters on H5 and a single stray subsection on H3; treating H3 as "the" section level (or any fixed level) turned the entire back half of the document into one fake 6,601-line "section". Re-run against that same document with this change: 12 correctly-sized real sections, not one. build_doc_index() now also computes retrieval_sections -- headings grouped at exactly the inferred level (off-level stray headings stay inside whichever section they geographically fall under, rather than splitting one apart), each with a SHA-256 hash of its own text. diff_stale_sections() compares a file's current content against its last cached build at this granularity and reports which sections actually changed, matched by position (not heading text -- duplicate titles are real, see toc-heading-duplicate) -- the piece needed for a future caller to re-summarize only what changed instead of the whole document. Registered the three new instructions in traceability-validation.md; whitelisted diff_stale_sections in vulture_whitelist.py alongside annotate_section_summary (same "future caller, exercised by tests" situation). New code is 100% covered; existing sections/annotate/etag behavior is untouched and still passing. See constructorfabric#104. Verified: pytest (test_doc_index.py + test_toc.py: 156 passed; full suite: 4815 passed, the same 12 pre-existing macOS-local/flaky failures as on main, none in the files touched here), pylint and vulture clean, cfs validate 0 errors, spec-coverage thresholds met, and infer_section_level re-run against the real PDF-converted document that originally exposed the bug. Signed-off-by: TECK KEAT WILSON <yeow.teck.keat@constructor.tech>
…abric#109 - cmd_doc_index() built its output from the index but omitted retrieval_sections/section_level in both JSON and human output -- the new data constructorfabric#109 added was invisible through the CLI. Both are exposed now, and the human formatter lists retrieval sections the same way it already lists the finer-grained ones. - A write landing between read_text() and _compute_etag() in build_doc_index() could save headings parsed from the *old* content stamped with the *new* file's etag; load_doc_index() would then treat that stale index as valid until a later edit changed the etag again. _read_with_stable_etag() brackets the read with a stat snapshot on each side and retries on mismatch, so the saved etag is provably the one that matches what was actually parsed. - diff_stale_sections() reported changed/unchanged sections by heading text alone; two sections sharing a duplicate title (a real, already-flagged possibility -- see toc-heading-duplicate) couldn't be told apart. Each entry now carries line_start alongside the heading text, which is what a caller should actually use to address "this specific section" afterwards. - annotate_section_summary() updated only index["sections"], leaving the matching retrieval_sections entry at summary=None even on success -- a caller reading retrieval_sections (the more relevant list for a future per-section summarizer) couldn't see the annotation. Now updates both when both have an entry at line_start. - toc.py's _frontmatter_has_description() accepted `description: # TODO` and `description: ""` as satisfying the check, since `#` and `"` both match \S. Now parses the field's actual value and rejects comments and empty/whitespace-only quoted strings. Extracted _compute_fresh_retrieval_sections/_position_entry out of diff_stale_sections() to stay under pylint's local-variable limit after the line_start addition; registered the new instructions (stable-read, diff-stale-helpers) in traceability-validation.md. See constructorfabric#104. Verified: pytest (test_doc_index.py + test_toc.py: 166 passed, 100% coverage on touched doc_index files); full suite: 4825 passed, the same 12 pre-existing macOS-local/flaky failures as on constructorfabric#108/constructorfabric#109, none in files touched here; pylint and vulture clean; cfs validate 0 errors; spec-coverage thresholds met; infer_section_level/retrieval_sections re-verified against the real PDF-converted document that originally exposed the granularity bug -- still 12 correct sections. Signed-off-by: TECK KEAT WILSON <yeow.teck.keat@constructor.tech>
…torfabric#109 - load_doc_index() returned a cached index whenever its etag matched, with no check that the cached shape matched what this version of the code expects. A cache written before section_level/retrieval_sections existed can still have a matching etag if the file hasn't changed since -- cmd_doc_index() would then hit a KeyError reading those fields on a legacy cache instead of a clean rebuild. Now treated the same as a stale cache: rebuilt, not returned as-is. - _frontmatter_has_description() treated a YAML block-scalar marker (`description: |`, `description: >-`, ...) as a usable value, when the real content -- if any -- belongs on indented lines below it, not on the marker's own line. Now checks the first non-blank following line for real indentation before counting it as a description. See constructorfabric#104. Verified: pytest (test_doc_index.py + test_toc.py: 172 passed, 100% coverage on touched doc_index files); full suite: 4831 passed, the same 12 pre-existing macOS-local/flaky failures as before, none in files touched here; pylint and vulture clean; cfs validate 0 errors; spec-coverage thresholds met; infer_section_level/retrieval_sections re-verified against the real PDF-converted document -- still 12 correct sections. Signed-off-by: TECK KEAT WILSON <yeow.teck.keat@constructor.tech>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on constructorfabric#108 (constructorfabric#108)
This branches from
jit-retrieval-doc-index, the branch behind constructorfabric#108 — it needsdoc_index.py/toc.pyto exist first, so it's opened here (within this fork) rather than against upstream directly, since GitHub won't let a cross-repo PR base on a branch that only exists on the fork. Retarget toconstructorfabric/studio:mainonce constructorfabric#108 merges.Summary
Resolves two of constructorfabric#108's follow-up open questions (findings doc §13b/§13c).
infer_section_level(): picks which heading level represents one real retrievable section, using the level's frequency as the signal. A document's real recurring structure (its chapters) shows up as the level used most often; an occasional heading at an anomalous level — exactly what PDF-to-Markdown conversion produces, since it assigns levels by font-size heuristics, not semantic depth — is rare precisely because it's noise, not structure. Levels used only once are excluded as candidates outright.This is a direct, verified fix for a real failure found building feat(toc,doc-index): add JIT-retrieval readiness checks and cached doc index constructorfabric/studio#108: a real PDF-converted document put all 8 of its actual chapters on H5 and a single stray subsection on H3. Treating H3 as "the" section level (or any fixed level) turned the entire back half of the document into one fake 6,601-line "section". Re-run against that same document with this change: 12 correctly-sized real sections, not one.
retrieval_sections(new field on the built index): headings grouped at exactly the inferred level — off-level stray headings stay inside whichever section they geographically fall under, rather than splitting one apart — each with a SHA-256 hash of its own text.diff_stale_sections(): compares a file's current content against its last cached build at this granularity and reports which sections actually changed, matched by position (not heading text — duplicate titles are real, seetoc-heading-duplicate). This is the piece a future caller needs to re-summarize only what changed instead of the whole document — the whole-file etag from feat(toc,doc-index): add JIT-retrieval readiness checks and cached doc index constructorfabric/studio#108 can only say "something changed", not "what".Existing
sections/annotate_section_summary/etag behavior is untouched.Test plan
pytest tests/test_doc_index.py tests/test_toc.py— 156 passed, 100% coverage on touched filespylint/vultureclean;cfs validate0 errors;spec-coveragethresholds metinfer_section_levelre-run for real against the actual PDF-converted document that originally exposed the bug (see commit message)